#pragma once

#include <stx/core/lambda.hpp>

inline namespace stx
{
template<IsSizedRange T>
requires requires(RangeValueType<T> const& a, RangeValueType<T> const& b)
{
    requires requires
    {
        a <=> b;
    } || IsPod<decltype(a)>;
}
[[nodiscard]] constexpr std::strong_ordering
threeWayCompare__(T const& rng1, T const& rng2) noexcept
{
    auto const size_a = std::size(rng1);
    auto const size_b = std::size(rng2);

    auto const min_size = std::min(size_a, size_b);
    auto       pos1     = std::begin(rng1);
    auto       pos2     = std::begin(rng2);

    for (auto i = 0uz; i < min_size; ++i, ++pos1, ++pos2)
    {
        auto const& v1 = *pos1;
        auto const& v2 = *pos2;

        if constexpr (requires(RangeValueType<T> const& a,
                               RangeValueType<T> const& b) { a <=> b; })
        {
            if (v1 < v2)
            {
                return std::strong_ordering::less;
            }

            if (v1 > v2)
            {
                return std::strong_ordering::greater;
            }
        }
        else
        {
            auto const r = std::memcmp(&v1, &v2, sizeof(v1));

            if (r < 0)
            {
                return std::strong_ordering::less;
            }

            if (r > 0)
            {
                return std::strong_ordering::greater;
            }
        }
    }

    if (size_a == size_b)
    {
        return std::strong_ordering::equal;
    }

    return size_a < size_b ? std::strong_ordering::less
                           : std::strong_ordering::greater;
}

template<IsSizedRange T>
requires requires(RangeValueType<T> const& a, RangeValueType<T> const& b)
{
    requires requires
    {
        a <=> b;
    } || IsPod<decltype(a)>;
}
[[nodiscard]] constexpr auto operator<=>(T const& a, T const& b) noexcept
{
    return threeWayCompare__(a, b);
}

template<IsSizedRange T>
requires requires(RangeValueType<T> const& a, RangeValueType<T> const& b)
{
    requires requires
    {
        a <=> b;
    } || IsPod<decltype(a)>;
}
[[nodiscard]] constexpr bool operator==(T const& a, T const& b) noexcept
{
    return 0 == threeWayCompare__(a, b);
}
} // namespace stx

namespace std
{
using ssize_t = ptrdiff_t;
} // namespace std

namespace ranges
{
namespace views
{
struct CRefHelper__ final
{};

// std::string{} | views::cref | views::take(7) | views::to<std::vector>;
constexpr auto cref = CRefHelper__{};

[[nodiscard]] inline auto operator|(stx::IsRange auto const& rng,
                                    CRefHelper__ const&)
{
    return all(rng);
}

struct FnTrimFront__ final
{
    auto operator()(auto const pr1) const
    {
        return drop_while(pr1);
    }
};

struct FnTrimBack__ final
{
    auto operator()(auto const pr1) const
    {
        return reverse | drop_while(pr1) | reverse;
    }
};

constexpr auto const trim_front = FnTrimFront__{};
constexpr auto const trim_back  = FnTrimBack__{};
} // namespace views
} // namespace ranges